package com.gateway.bootstrap.convert;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gateway.common.dto.route.FilterData;
import com.gateway.common.dto.route.PredicateData;
import com.gateway.common.dto.route.RouteData;
import com.gateway.common.dto.sync.RouteFilterSyncData;
import com.gateway.common.dto.sync.RoutePredicateSyncData;
import com.gateway.common.dto.sync.RouteSyncData;
import com.gateway.common.enums.DeletedEnum;
import com.gateway.common.exception.GatewayException;
import com.gateway.common.utils.JsonUtils;
import com.gateway.plugin.base.convert.RouteDataConvert;
import com.gateway.repository.bean.GatewayFilterDefinition;
import com.gateway.repository.bean.GatewayPredicateDefinition;
import com.gateway.repository.bean.GatewayRouteDefinition;
import com.gateway.repository.service.GatewayFilterDefinitionService;
import com.gateway.repository.service.GatewayPredicateDefinitionService;
import com.gateway.repository.service.GatewayRouteDefinitionService;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @program: api-gateway
 * @description:
 * @author: YuKai Fan
 * @create: 2025/2/6 10:32
 **/
@Component
public class RouteDataConvertFunction implements RouteDataConvert {
    private static final Logger log = LoggerFactory.getLogger(RouteDataConvertFunction.class);
    @Resource
    private GatewayRouteDefinitionService gatewayRouteDefinitionService;
    @Resource
    private GatewayFilterDefinitionService gatewayFilterDefinitionService;
    @Resource
    private GatewayPredicateDefinitionService gatewayPredicateDefinitionService;

    @Override
    public RouteData apply(RouteSyncData routeSyncData) {
        GatewayRouteDefinition gatewayRouteDefinition = gatewayRouteDefinitionService.getOne(
                new QueryWrapper<GatewayRouteDefinition>().lambda()
                        .eq(GatewayRouteDefinition::getEnv, routeSyncData.getEnv())
                        .eq(GatewayRouteDefinition::getRouteId, routeSyncData.getRouteId())
                        .eq(GatewayRouteDefinition::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(gatewayRouteDefinition)) {
            log.error("RouteDataConvertFunction query Gateway Route empty, syncData={}", routeSyncData);
            return null;
        }
        List<GatewayFilterDefinition> gatewayFilterDefinitions = gatewayFilterDefinitionService.list(
                new QueryWrapper<GatewayFilterDefinition>().lambda()
                        .eq(GatewayFilterDefinition::getEnv, routeSyncData.getEnv())
                        .eq(GatewayFilterDefinition::getRouteId, routeSyncData.getRouteId())
                        .eq(GatewayFilterDefinition::getDeleted, DeletedEnum.NO.value)
        );
        RouteData routeData = convert(gatewayRouteDefinition);
        if (CollectionUtils.isNotEmpty(gatewayFilterDefinitions)) {
            List<FilterData> filterDataList = gatewayFilterDefinitions.stream()
                    .map(RouteDataConvertFunction::convert)
                    .toList();
            routeData.setFilters(filterDataList);
        }
        List<GatewayPredicateDefinition> gatewayPredicateDefinitions = gatewayPredicateDefinitionService.list(
                new QueryWrapper<GatewayPredicateDefinition>().lambda()
                        .eq(GatewayPredicateDefinition::getEnv, routeSyncData.getEnv())
                        .eq(GatewayPredicateDefinition::getRouteId, routeSyncData.getRouteId())
                        .eq(GatewayPredicateDefinition::getDeleted, DeletedEnum.NO.value)
        );
        if (CollectionUtils.isNotEmpty(gatewayPredicateDefinitions)) {
            List<PredicateData> predicateDataList = gatewayPredicateDefinitions.stream()
                    .map(RouteDataConvertFunction::convert)
                    .toList();
            routeData.setPredicates(predicateDataList);
        }
        return routeData;
    }

    @Override
    public FilterData apply(RouteFilterSyncData routeFilterSyncData) {
        GatewayFilterDefinition gatewayFilterDefinition = gatewayFilterDefinitionService.getOne(
                new QueryWrapper<GatewayFilterDefinition>().lambda()
                        .eq(GatewayFilterDefinition::getId, routeFilterSyncData.getFilterId())
                        .eq(GatewayFilterDefinition::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(gatewayFilterDefinition)) {
            return null;
        }
        return convert(gatewayFilterDefinition);
    }

    @Override
    public PredicateData apply(RoutePredicateSyncData predicateSyncData) {
        GatewayPredicateDefinition gatewayPredicateDefinition = gatewayPredicateDefinitionService.getOne(
                new QueryWrapper<GatewayPredicateDefinition>().lambda()
                        .eq(GatewayPredicateDefinition::getId, predicateSyncData.getPredicateId())
                        .eq(GatewayPredicateDefinition::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(gatewayPredicateDefinition)) {
            return null;
        }
        return convert(gatewayPredicateDefinition);
    }


    public static RouteData convert(GatewayRouteDefinition routeDefinition) {
        try {
            return RouteData.Builder.builder()
                    .id(routeDefinition.getRouteId())
                    .appId(routeDefinition.getAppId())
                    .appName(routeDefinition.getAppName())
                    .env(routeDefinition.getEnv())
                    .contextPath(routeDefinition.getContextPath())
                    .uri(new URI(routeDefinition.getUri()))
                    .rpcType(routeDefinition.getRpcType())
                    .metadata(JsonUtils.toObjectMap(routeDefinition.getMetadata()))
                    .order(routeDefinition.getSortNo())
//                    .filters(convertToFilter(routeDefinition.getFilterDefinitionConfig()))
//                    .predicates(convertToPredicate(routeDefinition.getPredicateDefinitionConfig()))
                    .serviceId(routeDefinition.getServiceId())
                    .metadata(StringUtils.isNotBlank(routeDefinition.getMetadata()) ? JsonUtils.toObjectMap(routeDefinition.getMetadata()): Collections.emptyMap())
                    .build();
        } catch (URISyntaxException e) {
            throw new GatewayException(e);
        }
    }

    public static FilterData convert(GatewayFilterDefinition filterDefinition) {
        return FilterData.Builder.builder()
                .handle(filterDefinition.getHandle())
                .args(JsonUtils.toObjectMap(filterDefinition.getArgs(), String.class))
                .name(filterDefinition.getName())
                .routeId(filterDefinition.getRouteId())
                .filterId(String.valueOf(filterDefinition.getId()))
                .build();
    }

    public static PredicateData convert(GatewayPredicateDefinition predicateDefinition) {
        return PredicateData.Builder.builder()
                .handle(predicateDefinition.getHandle())
                .args(JsonUtils.toObjectMap(predicateDefinition.getArgs(), String.class))
                .name(predicateDefinition.getName())
                .predicateId(String.valueOf(predicateDefinition.getId()))
                .routeId(predicateDefinition.getRouteId())
                .build();
    }

    private static List<FilterData> convertToFilter(String filterConfig) {
        return JsonUtils.jsonToList(filterConfig, FilterData.class);
    }

    private static List<PredicateData> convertToPredicate(String predicateConfig) {
        return JsonUtils.jsonToList(predicateConfig, PredicateData.class);
    }

}
